----------------------------------------------------------------------------------
-- Company:        RIT
-- Engineer:       Sam Skalicky
-- 
-- Create Date:    17:49:05 12/11/2009 
-- Design Name:    MSD P10662
-- Module Name:    Device - Behavioral 
-- Project Name:   Inout
-- Target Devices: Spartan 6 LXT
-- Tool versions:  QuestaSim-64 6.4c
-- Description:    This a bi-directional I2C/Two-wire port implementation in VHDL
--
-- Notes: Data is the bi-directional port, Clk is typeset as an inout in the idea that
-- the clock could be generated to this port (Master device) or recieved from this port
-- in the case of a Slave device. However it is not meant to be a bi-directional port
--
-- Dependencies:   None
--
-- Revision: 1.0
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity I2Cslave is
    port( --inputs from bus
          I2CData : inout std_logic;
          I2CClk : in std_logic;
          --inputs from device (setup)
          I2CAddress : in std_logic_vector(6 downto 0);
          SendData : in std_logic_vector(7 downto 0);
          FPGA_CLK : in std_logic;
          --outputs to device
          Operation : out std_logic;
          RegID : out std_logic_vector(7 downto 0);
          RecData : out std_logic_vector(7 downto 0);
          Operation_Valid : std_logic:='0';
          RegID_Valid : out std_logic:='0';
          RecData_Valid : out std_logic:='0'
        );
end I2Cslave;

architecture state7 of I2Cslave is

  component I2Cports is
    Port ( Dir : in  STD_LOGIC; --0 is out, 1 is in
           Clk : inout  STD_LOGIC;
			     Data : inout STD_LOGIC:='0');
  end component;

  type states is (idle,recieve,addrCheck,ack,write,read,recAck);
  signal pstate,nstate : states:=idle;
  signal dataDir,clkI2C,data,ackSignal,clk : std_logic:='1';
  signal counter : std_logic_vector(4 downto 0):=(others=>'0');
  signal shiftReg,i2cAddr,rID,rData,sData : std_logic_vector(7 downto 0);
  signal myAddress : std_logic_vector(6 downto 0);
  signal vid,vdata : std_logic:='0';
begin
  
  clk<=FPGA_CLK;
  clkI2C<=I2CCLK;
  data<=I2CData;
  myAddress<=I2CAddress;
  RegID_Valid<=vid;
  RegID<=rID;
  RecData_Valid<=vdata;
  RecData<=rData;
  
  I2Cport: I2Cports port map(dataDir,clk,data);
  
  ------------
  --Control
  ------------
  process (clkI2C,data,nstate)
    
  begin
    if clkI2C='1' then
	   case nstate is
		   when idle =>	--state changes when start bit received
			   if falling_edge(data) then
				   nstate<=recieve;
			   end if;
		   when recieve =>		--receive 8 bits
			   if counter = "01000" then
			     if pstate = idle then
				    nstate<=addrCheck;
				   elsif pstate = ack then
				    nstate<=ack;
				   end if;
				   counter<=counter+1;
				 elsif counter = "10000" then
				   nstate<=ack;
				   counter<=counter+1;
				 elsif counter = "11000" then
				   nstate<=ack;
				   counter<=counter+1;
		    	end if;
	    	when addrCheck =>
			   if shiftReg(6 downto 0) = myAddress then
				  nstate<=ack;
				 else
				  nstate<=idle;
			   end if;
		   when ack =>
		     case counter is
		       when "01001" =>
		         nstate<=recieve;
		       when "10001" =>
		         if shiftReg(7) = '1' then --read  (1)
		           nstate<=read;
		         else                       --write (0)
		           nstate<=recieve;
		         end if;	
		       when "11001" =>
		         nstate<=write;     
		       when others => null;
		     end case;
		   when write =>
		     nstate<=idle;
		   when read =>
		     if counter > "11000" then
		       nstate<=recAck;
		     end if;
		   when recAck =>
		     if ackSignal = '1' then
		       nstate<=idle;
		     else
		       nstate<=read;
		     end if;
	   end case;
	   pstate<=nstate;
    end if;
  end process;
  
  ------------
  --Datapath
  ------------
  process (nstate,clkI2C) 
  begin
    if clkI2C='1' then
      case nstate is
        when idle => 
          counter<=(others=>'0');
        when recieve =>
          shiftReg<=data & shiftReg(7 downto 1);
          counter<=counter+1;
          --move data to specific register & set valid bit
          case counter is
            when "01000" => --i2c address
              i2cAddr<=shiftReg;
            when "10000" => --regID
              rID<=shiftReg;
              vid<='1';
            when "11000" => --received data
              rData<=shiftReg;
              vdata<='1';
            when others => null;
          end case;
        when addrCheck => null;
        when ack =>
          --send ack (pull sda low until falling edge of clk)
          if rising_edge(clkI2C) then
            dataDir<='0';
            data<='0';
          elsif falling_edge(clkI2C) then
            data<='1';
            dataDir<='1';
          end if;
          sData<=SendData;
        when write => null;
        when read =>
          dataDir<='0';
          if falling_edge(clkI2C) then
            data<=sData(0);
            sData<='0'&sData(7 downto 1);
          end if;
        when recAck =>
          dataDir<='1';
      end case;
    end if;
  end process;
  
end state7;